/*
 * 2006-1-6 lxm 创建。 
 */
package com.hd123.rumba.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.runtime.FormatSettings;
import com.hd123.rumba.runtime.IXMLPersistent;

/**
 * 关于XML工具集。
 * 
 * @author lxm
 * 
 */
public class XMLUtil {

  /**
   * 构造一个空的XML DOM Document对象。
   * 
   * @return
   * @throws BusinessException
   */
  public static Document newDocument() throws BusinessException {
    try {
      DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      return builder.newDocument();
    } catch (ParserConfigurationException e) {
      throw new BusinessException(e);
    }
  }

  /**
   * 将XML从DOM对象结构转换为字符串。
   * 
   * @param doc
   *          指定包含XML DOM对象的Document对象。
   * @return XML的字符串。
   * @throws BusinessException
   */
  public static String transform(Document doc) throws BusinessException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    XMLUtil.transform(doc, out);
    return out.toString();
  }

  /**
   * 将XML从DOM对象结构进行转换。
   * 
   * @param doc
   *          指定包含XML DOM对象的Document对象。
   * @param out
   *          指定作为输出的流。
   * @throws BusinessException
   */
  public static void transform(Document doc, OutputStream out) throws BusinessException {
    Transformer t;
    try {
      t = TransformerFactory.newInstance().newTransformer();
      t.transform(new DOMSource(doc), new StreamResult(out));
    } catch (TransformerConfigurationException e) {
      throw new BusinessException(e);
    } catch (TransformerException e) {
      throw new BusinessException(e);
    }
  }

  /**
   * 解析XML。
   * 
   * @param inStr
   *          指定包含XML的字符串。
   * @return 返回包含XML DOM对象的Document对象。
   * @throws BusinessException
   */
  public static Document parse(String inStr) throws BusinessException {
    ByteArrayInputStream in = new ByteArrayInputStream(inStr.getBytes());
    return XMLUtil.parse(in);
  }

  /**
   * 解析XML。
   * 
   * @param in
   *          指定包含XML的输入流。
   * @return 返回包含XML DOM对象的Document对象。
   * @throws BusinessException
   */
  public static Document parse(InputStream in) throws BusinessException {
    DocumentBuilder builder;
    try {
      builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
      return builder.parse(in);
    } catch (ParserConfigurationException e) {
      throw new BusinessException(e);
    } catch (SAXException e) {
      throw new BusinessException(e);
    } catch (IOException e) {
      throw new BusinessException(e);
    }
  }

  /**
   * 将String类型的值写入XML。
   * 
   * @param element
   * @param name
   * @param value
   */
  public static void writeString(Element element, String name, String value) {
    if (value != null)
      element.setAttribute(name, value);
  }

  /**
   * 从XML中读取一个String的值。
   * 
   * @param element
   * @param name
   * @return
   */
  public static String readString(Element element, String name) {
    return element.getAttribute(name);
  }

  /**
   * 将Integer类型的值写入XML。
   * 
   * @param element
   * @param name
   * @param value
   */
  public static void writeInteger(Element element, String name, Integer value) {
    if (value != null)
      element.setAttribute(name, value.toString());
  }

  /**
   * 从XML中读取一个Integer的值。
   * 
   * @param element
   * @param name
   * @return
   */
  public static Integer readInteger(Element element, String name) {
    String value = element.getAttribute(name);
    return value == null ? null : new Integer(value);
  }

  /**
   * 将BigDecimal类型的值写入XML。
   * 
   * @param element
   * @param name
   * @param value
   */
  public static void writeBigDecimal(Element element, String name, BigDecimal value) {
    if (value != null)
      element.setAttribute(name, value.toString());
  }

  /**
   * 从XML中读取一个BigDecimal的值。
   * 
   * @param element
   * @param name
   * @return
   */
  public static BigDecimal readBigDecimal(Element element, String name) {
    String value = element.getAttribute(name);
    return value == null ? null : new BigDecimal(value);
  }

  /**
   * 将Date类型的值写入XML。
   * 
   * @param element
   * @param name
   * @param value
   */
  public static void writeDate(Element element, String name, Date value) {
    if (value != null)
      element.setAttribute(name, FormatSettings.formatDatetime(FormatSettings.SDFyMdHms, value));
  }

  /**
   * 从XML中读取一个Date的值。
   * 
   * @param element
   * @param name
   * @return
   * @throws ParseException
   */
  public static Date readDate(Element element, String name) throws ParseException {
    String value = element.getAttribute(name);
    if (value == null)
      return null;
    else
      return FormatSettings.parseDatetime(FormatSettings.SDFyMdHms, value);
  }

  /**
   * 将Boolean类型的值写入XML。
   * 
   * @param element
   * @param name
   * @param value
   */
  public static void writeBoolean(Element element, String name, Boolean value) {
    if (value != null)
      element.setAttribute(name, value.toString());
  }

  /**
   * 从XML中读取一个Boolean的值。
   * 
   * @param element
   * @param name
   * @return
   */
  public static Boolean readBoolean(Element element, String name) {
    String value = element.getAttribute(name);
    if (value == null || value.equals(""))
      return null;
    else
      return value.equalsIgnoreCase("true") ? Boolean.TRUE : Boolean.FALSE;
  }

  /**
   * 将一个对象写入XML。
   * 
   * @param doc
   * @param element
   *          对象所属父元素。
   * @param obj
   */
  public static void writeObject(Document doc, Element element, IXMLPersistent obj)
      throws BusinessException {
    Element eObj = doc.createElement(obj.getXMLTagName());
    obj.writeToXML(doc, eObj);
    element.appendChild(eObj);
  }

  /**
   * 将对象明细（列表）写入XML。
   * 
   * @param doc
   * @param element
   * @param list
   *          List of IXMLPersistent
   */
  public static void writeDetails(Document doc, Element element, List list)
      throws BusinessException {
    for (int i = 0; i < list.size(); i++) {
      IXMLPersistent d = (IXMLPersistent) list.get(i);
      writeObject(doc, element, d);
    }
  }

  /**
   * 取值指定节点的直接下级Element，且tagName等于指定的tagName。
   * 
   * @param nParent
   * @param tagName
   * @return
   */
  public static List<Element> getChildElementsByTagName(Node nParent, String tagName) {
    List<Element> list = new ArrayList();
    NodeList nl = nParent.getChildNodes();
    for (int i = 0; i < nl.getLength(); i++) {
      Node n = nl.item(i);
      if (!(n instanceof Element))
        continue;
      Element e = (Element) n;
      if (e.getTagName().equals(tagName))
        list.add(e);
    }
    return list;
  }

}
